/**
 * des:工具函数库
 * author:何其沆
 */

/**
 * 冒泡排序
 * @param arr
 */
function bubbleSort(arr) {
  if (arr.length <= 1) return arr
  for (let i = 0; i < arr.length - 1; i++) {
    for (let j = 0; j < arr.length - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]]
    }
  }
  return arr
}

/**
 * 选择排序
 * @param arr
 */
function selectionSort(arr) {
  if (arr.length <= 1) return arr
  for (let i = 0; i < arr.length - 1; i++) {
    let minIndex = i
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[j] < arr[minIndex]) minIndex = j
    }
    [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]]
  }
  return arr
}

/**
 * 插入排序
 * @param arr
 */
function insertionSort(arr) {
  if (arr.length <= 1) return arr
  for (let i = 0; i < arr.length - 1; i++) {
    const current = arr[i + 1]
    let preIndex = i
    while (preIndex >= 0 && current < arr[preIndex]) {
      arr[preIndex + 1] = arr[preIndex]
      preIndex--
    }
    arr[preIndex + 1] = current
  }
  return arr
}

/**
 * 快速排序
 * @param arr
 */
function quickSort(arr) {
  if (arr.length <= 1) return arr
  const list = [...arr]
  const pivot = list[Math.floor(list.length / 2)]
  const left = list.filter(item => item < pivot)
  const center = list.filter(item => item === pivot)
  const right = list.filter(item => item > pivot)
  return [...quickSort(left), ...center, ...quickSort(right)]
}

/**
 * 深拷贝
 * @param target
 * @param map
 */
function deepClone(target, map = new WeakMap()) {
  const reference = [Date, Error, RegExp, Set, Map, WeakSet, WeakMap]
  if (map.has(target)) return map.get(target)
  let dist = null
  if (target instanceof Object) {
    if (Array.isArray(target)) dist = []
    else if (reference.includes(target.constructor)) dist = new target.constructor(target)
    else if (target && typeof target === 'object') dist = {}
    else dist = target
    map.set(target, target)
    for (let key in target) {
      if (target.hasOwnProperty(key)) dist[key] = deepClone(target[key], map)
    }
    return dist
  } else {
    return target
  }
}

/**
 * 防抖
 * @param fn
 * @param timeout
 */
function debounce(fn, timeout) {
  let timer = null
  return function (...args) {
    clearTimeout(timer)
    timer = setTimeout(() => {
      fn && fn.apply(this, args)
    }, timeout)
  }
}

/**
 * 节流
 * @param fn
 * @param timeout
 * @returns {(function(...[*]): void)|*}
 */
function throttle(fn, timeout) {
  let timer = null
  return function (...args) {
    if (timer) return
    timer = setTimeout(() => {
      fn && fn.apply(this, args)
      timer = null
    }, timeout)
  }
}

/**
 * 数组去重
 * @param arr
 */
function unique(arr) {
  const newData = arr.map(item => JSON.stringify(item))
  return [...new Set(newData)].map(item => JSON.parse(item))
  // return [...new Set(arr)]
  // return Array.from(new Set(arr))
  /*const obj = {}
  const res = []
  for (let i = 0; i < arr.length; i++) {
    if (!obj[arr[i]]) {
      obj[arr[i]] = 1
      res.push(arr[i])
    }
  }
  return res*/
  /*const res = []
  for (let i = 0; i < arr.length; i++) {
    if (!res.includes(arr[i])) res.push(arr[i])
  }
  return res*/
  /*const newArr = arr.reduce((pre, cur) => {
    return pre.includes(cur) ? pre : pre.concat(cur)
  }, [])*/
}

/**
 * 扁平化数组
 * @param arr
 */
function flatten(arr) {
  // return arr.flat(Infinity)
  /*while (arr.some(item => Array.isArray(item))) {
    arr = [].concat(...arr)
  }
  return arr*/
  let result = []
  for (let i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      result = result.concat(flatten(arr[i]))
    } else {
      result.push(arr[i])
    }
  }
  return result
}

/**
 * 统计数组中每个元素出现个数
 * @param arr
 */
function getCount(arr) {
  /*const obj = {}
  for (let i = 0; i < arr.length; i++) {
    if (!obj[arr[i]]) {
      obj[arr[i]] = 1
    } else {
      obj[arr[i]]++
    }
  }
  return obj*/
  return arr.reduce((total, value) => {
    if (value in total) {
      total[value]++
    } else {
      total[value] = 1
    }
    return total
  }, {})
}

/**
 * 模拟原生new
 * @param fn
 * @param args
 * @private
 */
function _new(fn, ...args) {
  const obj = Object.create(fn.prototype)
  const result = fn.call(obj, ...args)
  return Object.prototype.toString.call(result) === '[object Object]' ? result : obj
}

/**
 * 模拟原生instanceof
 * @param L
 * @param R
 */
function instanceOf(L, R) {
  const types = [String, Number, Boolean, Object, Array, Function, Symbol, Set, Map, WeakSet, WeakMap]
  if (typeof R !== 'object' && typeof R !== 'function') {
    throw new TypeError('Right-hand side of \'instanceof\' is not an object')
  } else if (!types.includes(R)) {
    throw new TypeError('Right-hand side of \'instanceof\' is not callable')
  }
  if (typeof L !== 'object' && typeof L !== 'function') return false
  if (L === null) return false
  let proto = L.__proto__
  const prototype = R.prototype
  while (true) {
    if (proto === null) return false
    if (proto === prototype) return true
    proto = proto.__proto__
  }
}

/**
 * 合并数组(特定条件)
 * @param arr1
 * @param arr2
 */
function mergeArr(arr1, arr2) {
  const set = new Set(arr1.map(item => item.id))
  const map = new Map(arr2.map(item => ([item.id, item])))
  return arr1.map(item => {
    if (map.has(item.id)) {
      return {...item, ...map.get(item.id)}
    }
    return {...item}
  }).concat(...arr2.filter(item => !set.has(item.id)))
}

/**
 * 数组转树
 * @param arr
 * @param pid
 */
function arrayToTree(arr, pid) {
  const res = []
  arr.forEach(item => {
    if (item.pid === pid) {
      const itemChildren = arrayToTree(arr, item.id)
      if (itemChildren.length > 0) {
        item.children = itemChildren
      }
      res.push(item)
    }
  })
  return res
}

/**
 * 树转数组
 * @param tree
 */
function treeToArray(tree) {
  const stack = tree
  const result = []
  while (stack.length !== 0) {
    let pop = stack.pop()
    result.push({
      id: pop.id,
      name: pop.name,
      pid: pop.pid
    })
    const children = pop.children
    if (children) {
      for (let i = children.length - 1; i >= 0; i--) {
        stack.push(children[i])
      }
    }
  }
  return result
}
